package org.example.input

import org.apache.commons.lang3.StringUtils
import org.example.client.DbClient
import org.example.common.Logging
import org.example.constant.ApolloConst
import org.example.dao.{RiskInfo, RiskRuleTrigger, RiskRuleWarning}
import org.example.utils.RedisSink
import scalikejdbc.SQL

import scala.collection.JavaConverters._

/**
 * 风险规则相关存放更新到redis
 *
 */
object RiskRule extends Logging {
  //是否启用风险:1是、0否
  private val IS_ENABLED = 1
  //是否删除:1是、0否
  private val IS_NOT_DELETE = 0
  //private val RISK_INFO_SQL = "select id,risk_name,risk_type_code,is_short_time_high_frequency,algorithm_cycle,vehicle_type from zcov.risk_info where is_enabled = ? and is_deleted = ?"
  //private val RISK_RULE_SQL = "select u.id,u.risk_id,u.`level` from zcov.risk_info r inner join zcov.risk_rule u on r.id = u.risk_id and u.is_enabled = ? where r.is_enabled = ? and r.is_deleted = ?"
  //private val RISK_RULE_WARNING_SQL = "select w.id,w.rule_id,w.warning_type_code,f.warning_field_code,f.expression_code,f.field_value from zcov.risk_rule_warning w inner join zcov.risk_rule_warning_field f on w.id = f.risk_rule_warning_id"
  //private val RISK_DEPLOY_SQL = "select risk_id from zcov.risk_vehicle_deploy where plate_num = ? and plate_color = ?"

  private val WARN_TYPE_SQL = "select type_code,type_name from zcov.config_warning_type where status = 1"

  private val VEHICLE_INFO_SQL = "select plate_num,plate_color from zcov.basic_vehicle_info where status = 1 and deleted != 1"

  private val RISK_TIME_DEPLOY = "select duration_value from zcov.config_risk_time_config where duration_type = 2"

  //private val RISK_INFO_SQL = "select id,risk_name,risk_type_code,is_short_time_high_frequency,algorithm_cycle,vehicle_type from zcov.risk_info where is_enabled = ? and is_deleted = ?"
  /*//截止V2.3.0版本，废弃车辆管控类型字段
  private val RISK_INFO_SQL = "select id,risk_name,risk_type_code,is_short_time_high_frequency,algorithm_cycle,vehicle_type from zcov.risk_info where is_enabled = ? and is_deleted = ?"*/
  //V2.3.1版本，替换车辆使用性质判断
  private val RISK_INFO_SQL = "select id,risk_name,risk_type_code,is_short_time_high_frequency,algorithm_cycle,use_natures from zcov.config_risk_info where status = ? and deleted = ?"

  private val RISK_RULE_SQL = "select u.id,u.risk_id,u.level from zcov.config_risk_info r inner join zcov.config_risk_rule u on r.id = u.risk_id and u.status = ? where r.status = ? and r.deleted = ?"

  //private val RISK_RULE_WARNING_SQL = "select w.id,w.rule_id,w.warning_type_code,f.warning_field_code,f.expression_code,f.field_value from zcov.risk_rule_warning w inner join zcov.risk_rule_warning_field f on w.id = f.risk_rule_warning_id"
  private val RISK_RULE_WARNING_SQL = "select w.id,w.rule_id,w.warning_type_code,f.warning_field_code,f.expression_code,f.field_value from zcov.config_risk_rule_warning w inner join zcov.config_risk_rule_warning_field f on w.id = f.risk_rule_warning_id"

  //private val RISK_DEPLOY_SQL = "select risk_id from zcov.risk_vehicle_deploy where plate_num = ? and plate_color = ?"
  private val RISK_DEPLOY_SQL = "select risk_id from zcov.config_risk_vehicle_deploy where plate_no = ? and plate_color = ?"
  /* private val WARN_TYPE_SQL = "select type_code,type_name from zcov.warning_type where status = 1"
   private val VEHICLE_INFO_SQL = "select plate_num,plate_color from zcov.base_into_vehicle_info where status = 'NORMAL' and is_deleted != 1"
   private val RISK_TIME_DEPLOY = "select duration_value from zcov.base_risk_time_config where duration_type = 2"*/


  /**
   * 获取启用中的风险
   *
   * @return
   */
  def getRisks(): Map[Long, RiskInfo] = {
    //获取风险点
    val riskInfos = getRiskInfo()
    val riskMap = riskInfos.map(x => (x.id, x)).toMap
    //获取风险点触发规则
    val riskRules = getRiskRule()
    val riskRuleMap: Map[Long, List[RiskRuleTrigger]] = riskRules.groupBy(x => x.riskId)
    //获取风险规则报警配置
    val ruleWarnings = getRiskRuleWarning()
    val ruleWarningMap: Map[Long, List[RiskRuleWarning]] = ruleWarnings.groupBy(x => x.ruleId)

    val risks = riskMap.map { x =>
      //风险点对应启用的风险点触发规则
      val riskRule = riskRuleMap.get(x._1)
      val riskRules = riskRule.map { list =>
        //风险点触发规则对应的报警配置
        val triggers = list.map { x =>
          x.riskRules = ruleWarningMap.get(x.id)
          x
        }
        triggers
      }
      x._2.riskRules = riskRules
      x
    }

    warn("最新风险点获取成功！")
    risks
  }

  /**
   * 获取风险信息
   *
   * @return
   */
  def getRiskInfo(): Iterable[RiskInfo] = {
    DbClient.init("riskRule",
      ApolloConst.jgdMysqlDriver, ApolloConst.jgdMysqlURL,
      ApolloConst.jgdMysqlUserName,
      ApolloConst.jgdMysqlPassWord)
    val riskInfos = DbClient.usingDB("riskRule") {
      db => {
        db readOnly {
          implicit session => {
            SQL(RISK_INFO_SQL)
              .bind(IS_ENABLED, IS_NOT_DELETE)
              .map { rs =>
                //V2.3.0前版本
                //val vehType = rs.get[String](6)
                //var vehicleTypes: Option[List[String]] = None
                //V2.3.1版本，替换车辆使用性质判断
                val useNature = rs.get[String](6)
                var useNatures: Option[List[String]] = None
                if (StringUtils.isNotEmpty(useNature)) {
                  useNatures = Some(useNature.split(",").toList)
                }
                RiskInfo(rs.get[Long](1), rs.get[String](2), rs.get[String](3), rs.get[Boolean](4), rs.get[Option[Int]](5), useNatures, None)
              }.list()
              .apply()
          }
        }
      }
    }
    riskInfos
  }

  /**
   * 获取风险触发点信息
   *
   * @return
   */
  def getRiskRule(): List[RiskRuleTrigger] = {
    DbClient.init("riskRule",
      ApolloConst.jgdMysqlDriver, ApolloConst.jgdMysqlURL,
      ApolloConst.jgdMysqlUserName,
      ApolloConst.jgdMysqlPassWord)
    val riskRules = DbClient.usingDB("riskRule") {
      db => {
        db readOnly {
          implicit session => {
            SQL(RISK_RULE_SQL)
              .bind(IS_ENABLED, IS_ENABLED, IS_NOT_DELETE)
              .map(rs => RiskRuleTrigger(rs.get[Long](1), rs.get[Long](2), rs.get[String](3), None))
              .list()
              .apply()
          }
        }
      }
    }
    riskRules
  }

  /**
   * 获取风险的报警规则
   *
   * @return
   */
  def getRiskRuleWarning(): List[RiskRuleWarning] = {
    DbClient.init("riskRule",
      ApolloConst.jgdMysqlDriver, ApolloConst.jgdMysqlURL,
      ApolloConst.jgdMysqlUserName,
      ApolloConst.jgdMysqlPassWord)
    val ruleWarnings = DbClient.usingDB("riskRule") {
      db => {
        db readOnly {
          implicit session => {
            SQL(RISK_RULE_WARNING_SQL)
              .bind()
              .map(rs => RiskRuleWarning(rs.get[Long](1), rs.get[Long](2), rs.get[String](3), rs.get[String](4), rs.get[String](5), rs.get[String](6)))
              .list()
              .apply()
          }
        }
      }
    }
    ruleWarnings
  }

  /**
   * 获取车开启的风险点
   *
   * @return
   */
  def getRiskDeploy(vehicleNo: String, vehicleColor: String): List[Long] = {
    DbClient.init("riskRule",
      ApolloConst.jgdMysqlDriver, ApolloConst.jgdMysqlURL,
      ApolloConst.jgdMysqlUserName,
      ApolloConst.jgdMysqlPassWord)
    val riskIds = DbClient.usingDB("riskRule") {
      db => {
        db readOnly {
          implicit session => {
            SQL(RISK_DEPLOY_SQL)
              .bind(vehicleNo, vehicleColor)
              .map(rs => rs.get[Long](1))
              .list()
              .apply()
          }
        }
      }
    }
    riskIds
  }

  /**
   * 获取报警类型
   *
   * @return (报警类型 -> 报警名称)
   */
  def getBaseWarn(): Map[String, String] = {
    DbClient.init("riskRule",
      ApolloConst.jgdMysqlDriver, ApolloConst.jgdMysqlURL,
      ApolloConst.jgdMysqlUserName,
      ApolloConst.jgdMysqlPassWord)
    val warns = DbClient.usingDB("riskRule") {
      db => {
        db readOnly {
          implicit session => {
            SQL(WARN_TYPE_SQL)
              .bind()
              .map(rs => (rs.get[String](1), rs.get[String](2)))
              .list()
              .apply()
          }
        }
      }
    }
    warns.toMap
  }

  /**
   * 更新监控的车辆信息
   *
   * @return
   */
  def updateVehicleNos(redisSink: RedisSink): Unit = {
    DbClient.init("riskRule",
      ApolloConst.jgdMysqlDriver, ApolloConst.jgdMysqlURL,
      ApolloConst.jgdMysqlUserName,
      ApolloConst.jgdMysqlPassWord)
    //获取需要监控的车辆
    val vehicleNos = DbClient.usingDB("riskRule") {
      db => {
        db readOnly {
          implicit session => {
            SQL(VEHICLE_INFO_SQL)
              .bind()
              .map(rs => (rs.get[String](1), rs.get[String](2)))
              .list()
              .apply()
          }
        }
      }
    }
    //清除已删除车辆智能设备的最后上线时间
    delLastLoginTime(redisSink, "lastOnlineSmartDay", vehicleNos)
    //清除已删除车辆gps设备的最后上线时间
    delLastLoginTime(redisSink, "lastOnlineGpsDay", vehicleNos)
    //更新监控的车辆信息
    val vehicleNoMap = vehicleNos.map(x => (x._1 + "#" + x._2, "1")).toMap.asJava
    redisSink.usingRedis { redis =>
      redis.select(9)
      redis.del("monitorVehicle")
      redis.hmset("monitorVehicle", vehicleNoMap)
    }
  }

  /**
   * 已经删除的车辆清除最后上线时间
   *
   * @param redisSink
   * @param key
   * @param vehNos
   */
  def delLastLoginTime(redisSink: RedisSink, key: String, vehNos: List[(String, String)]): Unit = {
    val vehNoList: List[String] = vehNos.map(x => x._1 + "#" + x._2)
    val datas = redisSink.usingRedis(r => {
      r.select(9)
      r.hgetAll(key)
    })
    if (null != datas) {
      //过滤已经删除或者禁用的车辆
      val delFields = datas.asScala.toList.filter(x => !vehNoList.contains(x._1)).map(_._1)
      //已经删除的车辆清除最后上线时间
      if (delFields.nonEmpty) {
        redisSink.usingRedis { r =>
          r.select(9)
          r.hdel(key, delFields: _*)
          warn(s"清除最后上线时间:${delFields}")
        }
      }
    }
  }

  /**
   * 获取夜间报警时间区间
   *
   * @return
   */
  def getRiskTimeDeploy(): (Int, Int) = {
    DbClient.init("riskRule",
      ApolloConst.jgdMysqlDriver, ApolloConst.jgdMysqlURL,
      ApolloConst.jgdMysqlUserName,
      ApolloConst.jgdMysqlPassWord)
    val warns = DbClient.usingDB("riskRule") {
      db => {
        db readOnly {
          implicit session => {
            SQL(RISK_TIME_DEPLOY)
              .bind()
              .map(rs => (rs.get[String](1)))
              .first()
              .apply()
          }
        }
      }
    }
    val riskTimeDeploy = warns.getOrElse("22:00~06:00")
    val riskTimeDeploys = riskTimeDeploy.split("~")
    val start_time = riskTimeDeploys(0).split(":")(0).toInt * 60 + riskTimeDeploys(0).split(":")(1).toInt
    val end_time = riskTimeDeploys(1).split(":")(0).toInt * 60 + riskTimeDeploys(1).split(":")(1).toInt
    (start_time, end_time)
  }

}